#pragma once
#include <memory>
#include <vector>
#include "rtdev/rtdev.h"
namespace rtbus {
class RtCommuItf;
}
namespace rtdev {
class ScbRobPowerItf
{
public:
    virtual ~ScbRobPowerItf() = default;
    /**
     * @brief 机器人电源控制
     * @param power true表示通电，false表示断电
     * @param robid 机器人序号，-1表示所有机器人
     * @return 0表示成功，-1表示失败
     */
    virtual int set_power(bool power, int robid = -1) = 0;

    /**
     * @brief 获取机器人功率
     * @param robid 机器人序号，必须大于等于0
     * @return 机器人功率，单位W，失败返回-1
     */
    virtual double get_power(int robid) = 0;

    /**
     * @brief 获取机器人母线电压
     * @param robid 机器人序号，必须大于等于0
     * @return 机器人母线电压，单位V，失败返回-1
     */
    virtual double get_voltage(int robid) = 0;

    /**
     * @brief 获取机器人母线电流
     * @param robid 机器人序号，必须大于等于0
     * @return 机器人母线电流，单位A，失败返回-1
     */
    virtual double get_current(int robid) = 0;
};

class ScbCabItf
{
public:
    virtual ~ScbCabItf() = default;

    /**
     * @brief 获取机柜硬件版本号
     * @return 硬件版本号，失败返回0xffffffff
     * @note 
     */
    virtual int get_hw_ver() = 0;
    /**
     * @brief 获取机柜软件版本号
     * @return 硬件版本号，失败返回0xffffffff
     * @note 
     */
    virtual int get_sw_ver() = 0;

    /**
     * @addindex
     */
    virtual std::string get_version_str() = 0;

    /**
     * @brief 获取机柜ID编号
     * @return 机柜ID编号，失败返回0xffffffff
     */
    virtual int get_id() = 0;
    /**
     * @brief 设置机柜ID编号
     * @param id 机柜ID编号
     * @return 0表示成功，-1表示失败
     */
    virtual int set_id(uint32_t id) = 0;

    /**
     * @brief 获取机柜温度
     * @return 机柜温度，单位摄氏度,失败返回-1
     */
    virtual double get_temperature() = 0;

    /**
     * @brief 获取机柜电压
     * @return 机柜电压，单位V，失败返回-1
     */
    virtual double get_voltage() = 0;

    /**
     * @brief 获取机柜钳制电压
     * @param is_act 是否是实际电压，否则是指令电压
     * @return 机柜卡住电压，单位V，失败返回-1
     * @note minicab和robohubcab才有
     */
    virtual int get_stuck_voltage(bool is_act = true) = 0;

    /**
     * @brief 设置机柜钳制电压
     * @param voltage 电压值，单位V
     * @return 0表示成功，-1表示失败
     * @note minicab和robohubcab才有
     */
    virtual int set_stuck_voltage(int voltage) = 0;

    /**
     * @brief 是否有工控机关机请求
     * @return true表示有关机请求，false表示无关机请求
     */
    virtual bool is_poweroff_ipc() = 0;

    /**
     * @brief 请求关闭工控机。调用前确保控制器资源已经安全释放，数据已经保存完成
     * @return 0表示成功，-1表示失败
     */
    virtual int do_poweroff_ipc() = 0;

    /**
     * @brief 断开机器人通讯。用于本体升级或者其他需要断开机器人通讯的情况
     */
    virtual int disable_robot_commu(bool dis = true) = 0;

    /**
     * @brief 获取上电后运行的时间。
     * @return 时间，单位h。失败返回-1
     */
    virtual int get_current_run_time() = 0;

    /**
     * @brief 获取历史总计运行的时间。
     * @return 时间，单位h。失败返回-1
     */
    virtual int get_total_run_time() = 0;

    /**
     * @brief 设置供电电压类型
     * @param val 具体电压数值，如380,220,110,48,24
     * @param 成功返回0，失败返回-1
     * @todo 需要和固件确认maxcab等机柜的电压类型
     */
    virtual int set_power_supply(int val) = 0;

    /**
     * @brief 获取供电电压类型
     * @param 成功返回具体电压数值，失败返回-1
     * @todo 需要和固件确认maxcab等机柜的电压类型
     */
    virtual int get_power_supply() = 0;
};

class ScbSafetySignalItf
{
public:
    virtual ~ScbSafetySignalItf() = default;
    /**
     * @brief 获取系统急停信号状态
     * @param[out] dual_chn[2] 双路冗余的系统急停信号状态，true表示正常，false表示急停
     * @param[out] has_fault 是否有信号故障，必须双路不一致等
     */
    virtual int get_ei_stat(bool dual_chn[2], bool* has_fault) = 0;
    virtual int get_ext_ei_stat(bool dual_chn[2], bool* has_fault) = 0;
    virtual int get_si_stat(bool dual_chn[2], bool* has_fault) = 0;
    // struct SafeChnIOInfo
    // {
    //     uint8_t chnid;      //安全通道号
    //     uint8_t iotype;     //0:input,1:output
    //     uint8_t io0_index;  //冗余IO编号
    //     uint8_t io1_index;  //冗余IO编号
    // };
    // virtual std::vector<SafeChnIOInfo> get_channel_info() = 0;

    // struct SafeEvStat
    // {
    //     uint8_t ev;        //事件号
    //     int8_t chn_in;     //安全输入通道号,-1表示未关联
    //     bool value;        //由scb直接返回的状态，true表示高电平，false表示低电平
    //     bool dual_chn[2];  //双路冗余的IO信号状态，true表示高电平，false表示低电平
    //     bool has_fault;    //是否有信号故障，必须双路不一致等
    // };
    // /**
    //  * @brief 获取安全事件的IO状态
    //  * @param ev 事件号
    //  * @param[out] dual_chn[2] 双路冗余的IO信号状态，true表示高电平，false表示低电平
    //  * @param[out] has_fault 是否有信号故障，必须双路不一致等
    //  * @return 0表示成功，-1表示不合法事件号，-2表示事件未关联IO
    //  */
    // virtual int get_safeev_stat(uint8_t ev, bool dual_chn[2], bool* has_fault) = 0;

    // /**
    //  * @brief 触发安全事件，以让安全板输出安全信号
    //  * @param ev 事件号
    //  * @return 0表示成功，-1表示不合法事件号，-2表示事件未关联IO
    //  */
    // virtual int set_safeout_event(uint8_t ev) = 0;
    // /**
    //  * @brief 配置安全输入通道的关联事件
    //  * @param ev 事件号
    //  * @param chn_in 安全输入通道号，注意不是DI号，具体是哪些DI由不同的柜子决定
    //  * @return 0表示成功，-1表示不合法事件号，-2表示通道号不合法
    //  */
    // virtual int config_safein_event(uint8_t ev, uint8_t chn_in) = 0;
    // /**
    //  * @brief 配置安全输出通道的关联事件
    //  * @param ev 事件号
    //  * @param chn_in 安全输出通道号，注意不是DO号，具体是哪些DO由不同的柜子决定
    //  * @return 0表示成功，-1表示不合法事件号，-2表示通道号不合法
    //  */
    // virtual int config_safeout_event(uint8_t ev, uint8_t chn_out) = 0;

    virtual int set_tcp_offset(float x, float y, float z) = 0;
};

class ScbIOItf
{
public:
    virtual ~ScbIOItf() = default;
    virtual size_t dio_cnt(size_t& di_cnt, size_t& do_cnt) = 0;
    virtual size_t aio_cnt(size_t& ai_cnt, size_t& ao_cnt) = 0;

    virtual int set_do(std::vector<int8_t> output) = 0;
    virtual int get_di(std::vector<int8_t>& input) = 0;
    virtual int get_ao(std::vector<double> output) = 0;
    virtual int get_ai(std::vector<double>& input) = 0;
    virtual int get_encoder(uint8_t& dir, uint16_t& pulse) = 0;

    virtual int get_user_io_current() = 0;
    virtual int get_dio_type(uint8_t id) = 0;
    virtual int config_dio_type(uint16_t type) = 0;
    virtual int config_aio_type(uint16_t type) = 0;
    virtual int get_aio_type(uint8_t id) = 0;
};

const uint8_t kScbTypeCabV2 = 2;
const uint8_t kScbTypeMinicab = 3;
const uint8_t kScbTypeCabV3 = 4;
const uint8_t kScbTypeMaxcab = 5;
const uint8_t kScbTypeRoboHub = 6;

class SafetyBoardItf : public rtdev::RtDevItf
{
public:
    static SafetyBoardItf* create(uint8_t scbtype);

public:
    SafetyBoardItf();
    virtual ~SafetyBoardItf() = default;
    virtual int init(void* cfginfo, int sim) = 0;

    /**
     * @brief 断开安全板时调用与boot接口对应
     */
    int shutdown() override;
    bool is_alive() override;
    bool is_sim() override;
    virtual const char* name() = 0;                   //获取安全板名称
    virtual ScbRobPowerItf* rob_power() = 0;          //获取机器人电源监控接口
    virtual ScbCabItf* cab() = 0;                     //获取机柜监控接口
    virtual ScbSafetySignalItf* safety_signal() = 0;  //获取安全信号接口
    virtual ScbIOItf* io() = 0;                       //获取IO接口

public:  //安全板通讯数据交互接口
    virtual int set_send_data(int busid = -1) = 0;
    virtual int get_recv_data(int busid = -1) = 0;
    virtual int get_sdo_blocking(uint32_t objid, void* data, int s, int retry = 3) = 0;
    virtual int set_sdo_blocking(uint32_t objid, void* data, size_t s, void* readback = nullptr, int retry = 3) = 0;

protected:
    /**
     * @brief 检查安全板是否在线，该接口会根据安全板的心跳包（由调用方决定）来判断安全板是否在线。
     *        与keep_alive接口配合使用。默认为1000个周期，可以在子类中重设default_watchdog_来定制
     * @note 必须每个控制周期调用一次，建议在set_send_data接口内调用
     * @todo 內部实现的watchdog是按照调用次数来计算的，后面改为按照时间计算,最佳的方法是传入一个时间戳或毫秒计数值，千万不要内部使用系统时间戳
     */
    void check_alive() override;

    /**
     * @brief 保持安全板在线，该接口会根据安全板的心跳包来判断安全板是否在线。与check_alive接口配合使用，用于检查安全板是否在线
     * @param[in] alive 每个周期变化的心跳计数值，若是新的则内部会刷新安全板看门狗，否则不刷新
     * @return 0表示刷新安全板看门狗成功；-1表示失败
     */
    int keep_alive(int alive) override;

protected:
    uint8_t sim_flag_;
    int alive_watchdog_;
    bool alive_flag_;
    int last_alive_cnt_;

    int default_watchdog_;
};

class SafetyBoardFactoryItf
{
public:
    virtual ~SafetyBoardFactoryItf() = default;
    virtual SafetyBoardItf* create() = 0;
};

}  // namespace rtdev
